<?php
accessible_content_include_library();
module_load_include('inc', 'quail_server', 'includes/quail_server.htmlclass');

/**
*	Parses links from a provided HTML page. This no only supplies
*	a list of links, but also completes the links, making them absolute
*	links.
*	@param string $address The URL of the page
*	@param string $html The HTML code
*	@param array $options An aray of options. These are mainly enherited through website nodes
*	@return array An array of links
*/
function quail_server_parse_links($address, $html, $options = array()) {
	$links = array();
	$dom = new DOMDocument();
	$result = @$dom->loadHTML($html);
	if(!$result) {
		return $links;
	}
	foreach($dom->getElementsByTagName('a') as $a) {
		if($a->hasAttribute('href')) {
			if(substr($a->getAttribute('href'), 0, 1) != '#' && parse_url($a->getAttribute('href'))) {
				//We ignore anchor links to the same page
				$links[$a->getAttribute('href')] = $a->getAttribute('href');
			}
		}
	}
	foreach($dom->getElementsByTagName('frame') as $frame) {
		if($frame->hasAttribute('src')) {
			//We ignore anchor links to the same page
			$links[$frame->getAttribute('src')] = $frame->getAttribute('src');
		}
	}
	foreach($links as $k => $link) {
		$start = parse_url($options['start_uri']);
		if(!$options['span_hosts']) {
			$link_url = parse_url($link);
			if($link_url['host'] && $link_url['host'] != $start['host']) {
				unset($links[$k]);
			}
			elseif(!isset($link_url['host']) && $link_url['path']) {
				$links[$k] = quail_server_build_absolute($address, $link);
			} 
		}
	}
	return $links;
}

/**
*	Builds an absolute URL from a relative one.
*	@param string $absolute The URL of the current page
*	@param string $relative The URL of the link
*	@return string An absolute URL
*/
function quail_server_build_absolute($absolute, $relative) {
  $p = parse_url($relative);
  if ($p["scheme"]) {
    return $relative;
  }
  extract(parse_url($absolute));
  
  $path = dirname($path); 
  
  if ($relative{0} == '/') {
      $c_parts = array_filter(explode("/", $relative));
  }
  else {
    $a_parts = array_filter(explode("/", $path));
    $r_parts = array_filter(explode("/", $relative));
    $c_parts = array_merge($a_parts, $r_parts);
    foreach ($c_parts as $i => $part) {
      if ($part == '.') {
        $c_parts[$i] = NULL;
      }
      if ($part == '..') {
        $c_parts[$i - 1] = NULL;
        $c_parts[$i] = NULL;
      }
    }
    $c_parts = array_filter($c_parts);
  }
  $path = implode("/", $c_parts);
  $url = "";
  if ($scheme) {
    $url = "$scheme://";
  }
  if ($user) {
    $url .= "$user";
    if ($pass) {
      $url .= ":$pass";
    }
    $url .= "@";
  }
  if ($host) {
    $url .= "$host/";
  }
  $url .= $path;
  return $url;

}

/**
*	counts all the HTML nodes in provided HTML code
*	@param string $html The HTML code
*	@reutrn int The number of HTML nodes in the code.
*/
function _quail_server_count_nodes($html) {
	return substr_count($html, '<');
}

/**
*	Helper function to retrieve all the content
*	of a page that's ripped out of the template for the page
*	if that template applies to the current page.
*	@param string $html The HTML code to use
*	@param object $website The Website node associated with this page
*	@param string $uri The URI of this webpage
*	@param bool $make_whole_page If this is TRUE, then if content is removed
*				from this template, we then rebuild a whole HTML page for rendering later
*	@return string HTML code with the template data removed, or just the HTML code if 
*					no template was found.
*/
function _quail_server_get_content_from_template($html, $website, $uri, $make_whole_page = true) {
	$xpath = _quail_server_get_template_xpath($website, $uri);
	if(!$xpath) {
		return $html;
	}
	$dom = @DOMDocument::loadHTML($html);
	if(!$dom) {
		return $html;
	}
	$query = new DOMXPath($dom);
	$content = $query->query($xpath);
	if($content->length == 0) {
		return $html;
	}
	$content = $content->item(0);
	$body = $dom->getElementsByTagName('body')->item(0);
	foreach($body->childNodes as $child) {
		$body->removeChild($child);
	}
	$new = new DOMDocument();
	$new->appendChild($new->importNode($content, true));
	if($make_whole_page) {
		return '<html><head><title>'. _quail_server_get_title($html, 'Template').
		   '</title></head><body>'. $new->saveHTML() .'</body></html>';
	}
	return $new->saveHTML();
}

function _quail_server_get_template_xpath($website, $uri) {
	$templates = db_query('SELECT nid, xpath_query FROM quail_server_template
WHERE website_nid = %d AND "%s" REGEXP url_path', $website->nid, $uri);
	if($xpath_row = db_fetch_object($templates)) {
		return $xpath_row->xpath_query;
	}
	elseif($website->ac_server['default_template']['xpath_query']) {
		return $website->ac_server['default_template']['xpath_query'];
	}
	return false;
}

/**
*	Helper function to clenaup an xpath build by the javascript library.
*	@param string $query The XPATH query
*	@return string A cleaned up version
*/
function _quail_server_cleanup_xpath($query) {
	$remove = array(
			  '[@class=""]',
			  '[@class="ac_template_selected"]',
			  '[1]',
	);
	
	$query = str_replace('html[@class="js"]', 'html', $query);
	$query = str_replace($remove, '', $query);
	return $query;
}

function _quail_server_get_host_path($uri) {
	$url = parse_url($uri);
	return file_create_path() . '/ac_sites/'. $url['host'] .'/';
}

function _quail_server_get_title($html, $name) {
	$title = preg_match('/<title> ?.* <\/title>/isx', $html, $patterns);
	if(count($patterns)) {
		return strip_tags(html_entity_decode($patterns[0]));
	}
	return $name;
}

/**
*	Returns whether or not a page meets the requirements of a website's
*	excluded URL and depth rules.
*	@param object The website node object
*	@param mixed Either a string URL to check, or an array created by parse_url()
*/
function _quail_server_page_can_be_crawled($website, $url, $page) {
	if(!is_array($url)) {
		$url = parse_url($url);
	}
	if(_quail_server_is_error_page($url, $page, $website)) {
		return false;
	}
	$path = ltrim($url['path'], '/');
	if($website->ac_server['depth'] > 0 && 
		substr_count(rtrim($path, '/'), '/') > $website->ac_server['depth'] - 1) {
		return false;	
	}
	if($website->ac_server['excluded_paths'] && drupal_match_path($path, $website->ac_server['excluded_paths'])) {
		return false;
	}
	return true;
}

/**
*	Helper function which creates a QUAIL object and checks
*	for accessibility
*/
function quail_server_check_string($content, $uri, $guideline_nid, $cms_mode = FALSE) {
  if(strpos($content, 'html') === false) {
  	return false;
  }
  
  accessible_content_include_library();
  $display_level = array(
      QUAIL_TEST_SEVERE  => TRUE,
      QUAIL_TEST_MODERATE => TRUE,
      QUAIL_TEST_SUGGESTION => TRUE,
    );
  $guideline = node_load($guideline_nid);
  $quail = new quail($content, 'accessible_content', 'string', 'AccessibleContent');
  $quail->setOption('cms_mode', $cms_mode);
  if($quail->isValid()) {
	  $quail->setUri($uri);
	  $quail->runCheck(array('ac_module_guideline' => $guideline, 'display_level' => $display_level));
	  $report = $quail->getReport();
	  return $report;
  }
  return false;
}

function quail_server_get_website_score($node) {
	
}

/**
*	Returns a flat array of directory paths and file names. 
*	We leave this flat because it's just much easier to iterate over later.
*/
function _quail_server_get_directory($directory, $filter=FALSE) {
	if (substr($directory, -1) == '/') {
		$directory = substr($directory, 0, -1);
	}
	if (!file_exists($directory) || !is_dir($directory)) {
		return FALSE;
	}elseif (is_readable($directory)) {
		$directory_list = opendir($directory);
		while (FALSE !== ($file = readdir($directory_list))) {
			if ($file != '.' && $file != '..') {
				$path = $directory.'/'.$file;
				if (is_readable($path)) {
					$subdirectories = explode('/', $path);
					if (is_dir($path)) {
						$directory_tree[] = array(
							'path'    => $path,
							'name'    => end($subdirectories),
							'kind'    => 'directory',
							'content' => '');
						$directory_tree = array_merge($directory_tree, _quail_server_get_directory($path, $filter));
					}elseif (is_file($path)) {
						$extension = end(explode('.', end($subdirectories)));
						if ($filter === FALSE || $filter == $extension) {
							$directory_tree[] = array(
								'path'      => $path,
								'name'      => end($subdirectories),
								'extension' => $extension,
								'size'      => filesize($path),
								'kind'      => 'file');
						}
					}
				}
			}
		}
		closedir($directory_list);

		return $directory_tree;

	}else {
		return FALSE;
	}
}


/**
*	Because misconfigured servers can return 404 pages without
*	a 404 error code, we try and load a random page and see what 
*	comes out.
*/
function _quail_server_get_404_hash($url) {
	$result = drupal_http_request($url .'/'. md5($url));
	if($result->code == 200 || $result->redirect_code == 200) {
		return array('url' => $result->redirect_url,
					 'hash' => md5(trim($result->data)));
	}
	return array('url' => null, 'hash' => null);
}

/**
*	Returns whether or not the requested page is considered an
*	error page.
*/
function _quail_server_is_error_page($url, $page, $website) {
	return ($url == $website->ac_server['404_url'] || md5(trim($page)) == $website->ac_server['404_hash']);
}